home *** CD-ROM | disk | FTP | other *** search
/ Space & Astronomy / Space and Astronomy (October 1993).iso / mac / VIEWERS / AMIGA / GIFMACHN.LZH / rlpsrc / iffw.c < prev   
C/C++ Source or Header  |  1991-04-08  |  7KB  |  267 lines

  1. /*----------------------------------------------------------------------*
  2.  * IFFW.C  Support routines for writing IFF-85 files.          1/23/86
  3.  * (IFF is Interchange Format File.)
  4.  *
  5.  * By Jerry Morrison and Steve Shaw, Electronic Arts.
  6.  * This software is in the public domain.
  7.  *
  8.  * This version for the Commodore-Amiga computer.
  9.  *----------------------------------------------------------------------*/
  10. #include "iff/iff.h"
  11. #include "iff/gio.h"
  12. #include "proto/dos.h"
  13.  
  14. /* ---------- IFF Writer -----------------------------------------------*/
  15.  
  16. /* A macro to test if a chunk size is definite, i.e. not szNotYetKnown.*/
  17. #define Known(size)   ( (size) != szNotYetKnown )
  18.  
  19. /* Yet another weird macro to make the source code simpler...*/
  20. #define IfIffp(expr)  {if (iffp == IFF_OKAY)  iffp = (expr);}
  21.  
  22. /* ---------- OpenWIFF -------------------------------------------------*/
  23.  
  24. IFFP 
  25. OpenWIFF (BPTR file, GroupContext *new0, LONG limit)
  26. {
  27.     register GroupContext *new = new0;
  28.     register IFFP iffp = IFF_OKAY;
  29.  
  30.     new->parent = NULL;
  31.     new->clientFrame = NULL;
  32.     new->file = file;
  33.     new->position = 0;
  34.     new->bound = limit;
  35.     new->ckHdr.ckID = NULL_CHUNK;    /* indicates no current chunk */
  36.     new->ckHdr.ckSize = new->bytesSoFar = 0;
  37.  
  38.     if (0 > Seek (file, 0, OFFSET_BEGINNING))    /* Go to start of the file.*/
  39.         iffp = DOS_ERROR;
  40.     else if (Known (limit) && IS_ODD (limit))
  41.         iffp = CLIENT_ERROR;
  42.     return (iffp);
  43. }
  44.  
  45. /* ---------- StartWGroup ----------------------------------------------*/
  46. IFFP 
  47. StartWGroup (GroupContext *parent, ID groupType, LONG groupSize, ID subtype,
  48.          GroupContext *new)
  49. {
  50.     register IFFP iffp;
  51.  
  52.     iffp = PutCkHdr (parent, groupType, groupSize);
  53.     IfIffp (IFFWriteBytes (parent, (BYTE *) & subtype, sizeof (ID)));
  54.     IfIffp (OpenWGroup (parent, new));
  55.     return (iffp);
  56. }
  57.  
  58. /* ---------- OpenWGroup -----------------------------------------------*/
  59. IFFP 
  60. OpenWGroup (GroupContext *parent0, GroupContext *new0)
  61. {
  62.     register GroupContext *parent = parent0;
  63.     register GroupContext *new = new0;
  64.     register LONG ckEnd;
  65.     register IFFP iffp = IFF_OKAY;
  66.  
  67.     new->parent = parent;
  68.     new->clientFrame = parent->clientFrame;
  69.     new->file = parent->file;
  70.     new->position = parent->position;
  71.     new->bound = parent->bound;
  72.     new->ckHdr.ckID = NULL_CHUNK;
  73.     new->ckHdr.ckSize = new->bytesSoFar = 0;
  74.  
  75.     if (Known (parent->ckHdr.ckSize))
  76.     {
  77.         ckEnd = new->position + ChunkMoreBytes (parent);
  78.         if (new->bound == szNotYetKnown || new->bound > ckEnd)
  79.             new->bound = ckEnd;
  80.     };
  81.  
  82.     if (parent->ckHdr.ckID == NULL_CHUNK ||    /* not currently writing a chunk*/
  83.         IS_ODD (new->position) ||
  84.         (Known (new->bound) && IS_ODD (new->bound)))
  85.         iffp = CLIENT_ERROR;
  86.     return (iffp);
  87. }
  88.  
  89. /* ---------- CloseWGroup ----------------------------------------------*/
  90. IFFP 
  91. CloseWGroup (GroupContext *old0)
  92. {
  93.     register GroupContext *old = old0;
  94.     IFFP iffp = IFF_OKAY;
  95.  
  96.     if (old->ckHdr.ckID != NULL_CHUNK)    /* didn't close the last chunk */
  97.         iffp = CLIENT_ERROR;
  98.     else if (old->parent == NULL)
  99.     {            /* top level file context */
  100.         if (GWriteFlush (old->file) < 0)
  101.             iffp = DOS_ERROR;
  102.     }
  103.     else
  104.     {            /* update parent context */
  105.         old->parent->bytesSoFar += old->position - old->parent->position;
  106.         old->parent->position = old->position;
  107.     };
  108.     return (iffp);
  109. }
  110.  
  111. /* ---------- EndWGroup ------------------------------------------------*/
  112. IFFP 
  113. EndWGroup (GroupContext *old)
  114. {
  115.     register GroupContext *parent = old->parent;
  116.     register IFFP iffp;
  117.  
  118.     iffp = CloseWGroup (old);
  119.     IfIffp (PutCkEnd (parent));
  120.     return (iffp);
  121. }
  122.  
  123. /* ---------- PutCk ----------------------------------------------------*/
  124. IFFP 
  125. PutCk (GroupContext *context, ID ckID, LONG ckSize, BYTE *data)
  126. {
  127.     register IFFP iffp = IFF_OKAY;
  128.  
  129.     if (ckSize == szNotYetKnown)
  130.         iffp = CLIENT_ERROR;
  131.     IfIffp (PutCkHdr (context, ckID, ckSize));
  132.     IfIffp (IFFWriteBytes (context, data, ckSize));
  133.     IfIffp (PutCkEnd (context));
  134.     return (iffp);
  135. }
  136.  
  137. /* ---------- PutCkHdr -------------------------------------------------*/
  138. IFFP 
  139. PutCkHdr (GroupContext *context0, ID ckID, LONG ckSize)
  140. {
  141.     register GroupContext *context = context0;
  142.     LONG minPSize = sizeof (ChunkHeader);    /* physical chunk >= minPSize bytes*/
  143.  
  144.     /*
  145.      * CLIENT_ERROR if we're already inside a chunk or asked to
  146.      * write other than one FORM, LIST, or CAT at the top level of
  147.      * a file.
  148.      * Also, non-positive ID values are illegal and used for error
  149.      * codes. (We could check for other illegal IDs...)
  150.      */
  151.     if (context->ckHdr.ckID != NULL_CHUNK || ckID <= 0)
  152.         return (CLIENT_ERROR);
  153.     else if (context->parent == NULL)
  154.     {
  155.         switch (ckID)
  156.         {
  157.         case FORM:
  158.         case LIST:
  159.         case CAT:
  160.             break;
  161.         default:
  162.             return (CLIENT_ERROR);
  163.         }
  164.         if (context->position != 0)
  165.             return (CLIENT_ERROR);
  166.     }
  167.  
  168.     if (Known (ckSize))
  169.     {
  170.         if (ckSize < 0)
  171.             return (CLIENT_ERROR);
  172.         minPSize += ckSize;
  173.     };
  174.     if (Known (context->bound) &&
  175.         context->position + minPSize > context->bound)
  176.         return (CLIENT_ERROR);
  177.  
  178.     context->ckHdr.ckID = ckID;
  179.     context->ckHdr.ckSize = ckSize;
  180.     context->bytesSoFar = 0;
  181.     if (0 > GWrite (context->file,
  182.             (BYTE *) & context->ckHdr,
  183.             sizeof (ChunkHeader)) )
  184.         return (DOS_ERROR);
  185.     context->position += sizeof (ChunkHeader);
  186.     return (IFF_OKAY);
  187. }
  188.  
  189. /* ---------- IFFWriteBytes ---------------------------------------------*/
  190. IFFP 
  191. IFFWriteBytes (GroupContext *context0, BYTE *data, LONG nBytes)
  192. {
  193.     register GroupContext *context = context0;
  194.  
  195.     if (context->ckHdr.ckID == NULL_CHUNK    /* not in a chunk */
  196.         || nBytes < 0            /* negative nBytes */
  197.         || (Known (context->bound)        /* overflow context */
  198.             && context->position + nBytes > context->bound)
  199.         || (Known (context->ckHdr.ckSize)    /* overflow chunk */
  200.             && context->bytesSoFar + nBytes > context->ckHdr.ckSize)
  201.        )
  202.         return (CLIENT_ERROR);
  203.  
  204.     if (0 > GWrite (context->file, data, nBytes))
  205.         return (DOS_ERROR);
  206.  
  207.     context->bytesSoFar += nBytes;
  208.     context->position += nBytes;
  209.     return (IFF_OKAY);
  210. }
  211.  
  212. /* ---------- PutCkEnd -------------------------------------------------*/
  213. IFFP 
  214. PutCkEnd (GroupContext *context0)
  215. {
  216.     register GroupContext *context = context0;
  217.     WORD zero = 0;        /* padding source */
  218.  
  219.     if (context->ckHdr.ckID == NULL_CHUNK)    /* not in a chunk */
  220.         return (CLIENT_ERROR);
  221.  
  222.     if (Known(context->ckHdr.ckSize))
  223.     {
  224.         /*
  225.          * make sure the client wrote as many bytes as planned
  226.          */
  227.         if (context->ckHdr.ckSize != context->bytesSoFar)
  228.             return (CLIENT_ERROR);
  229.     }
  230.  
  231.     /*
  232.      * go back and set the chunk size to bytesSoFar
  233.      */
  234.     else if (0 > GSeek (context->file,
  235.                 -(context->bytesSoFar + sizeof (LONG)),
  236.                 OFFSET_CURRENT) )
  237.         return (DOS_ERROR);
  238.  
  239.     else if (0 > GWrite (context->file,
  240.                  (BYTE *) & context->bytesSoFar,
  241.                   sizeof (LONG)) )
  242.         return (DOS_ERROR);
  243.  
  244.     else if (0 > GSeek (context->file,
  245.                 context->bytesSoFar,
  246.                 OFFSET_CURRENT) )
  247.         return (DOS_ERROR);
  248.  
  249.     /*
  250.      * Write a pad byte if needed to bring us up to an even
  251.      * boundary. Since the context end must be even, and since we
  252.      * haven't overwritten the context, if we're on an odd position
  253.      * there must be room for a pad byte.
  254.      */
  255.  
  256.     if (IS_ODD (context->bytesSoFar))
  257.     {
  258.         if (0 > GWrite (context->file, (BYTE *) & zero, 1))
  259.             return (DOS_ERROR);
  260.         context->position += 1;
  261.     };
  262.  
  263.     context->ckHdr.ckID = NULL_CHUNK;
  264.     context->ckHdr.ckSize = context->bytesSoFar = 0;
  265.     return (IFF_OKAY);
  266. }
  267.